#include "SmileStruct.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include <mpich2/mpi.h>

#include <iostream>
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
#define LENGTH_LINGO 4
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
smileStruct::smileStruct():code(NULL), id(NULL), lingos(NULL), numberLingos(0) {}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
smileStruct::smileStruct(const char *code_tmp, const char *id_tmp): lingos(NULL), numberLingos(0) {

  code = new char[strlen(code_tmp) + 1];
  id   = new char[strlen(id_tmp) + 1];

  strcpy(code, code_tmp);
  strcpy(id, id_tmp);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
smileStruct::smileStruct(const smileStruct *smile_tmp) {

  code = new char[strlen(smile_tmp->code) + 1];
  id   = new char[strlen(smile_tmp->id) + 1];

  strcpy(code, smile_tmp->code);
  strcpy(id, smile_tmp->id);

  numberLingos = smile_tmp->numberLingos;

  lingos = new char*[numberLingos];
  for (int i = 0; i < numberLingos; i++) {
    lingos[i] = new char[strlen(smile_tmp->lingos[i]) + 1];
    strcpy(lingos[i], smile_tmp->lingos[i]);
  }

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
smileStruct::~smileStruct() {

  if (code) delete[] code;
  if (id)   delete[] id;

  if (lingos) {
    for (int i = 0; i < numberLingos; i++) if (lingos[i]) delete[] lingos[i];
    delete[] lingos;
  }

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::Code(char *code_tmp) {

  if (code) delete[] code;
  code = new char[strlen(code_tmp) + 1];
  strcpy(code, code_tmp);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::Id(char *id_tmp) {

  if (id) delete[] id;
  id = new char[strlen(id_tmp) + 1];
  strcpy(id, id_tmp);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::Lingos(char **lingos_tmp, int numberLingos_tmp) {

  if (lingos) {
    for (int i = 0; i < numberLingos; i++) if (lingos[i]) delete[] lingos[i];
    delete[] lingos;
  }

  lingos = new char*[numberLingos_tmp];
  for (int i = 0; i < numberLingos_tmp; i++) {
    lingos[i] = new char[strlen(lingos_tmp[i]) + 1];
    strcpy(lingos[i], lingos_tmp[i]);
  }

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::NumberLingos(int numberLingos_tmp) {

  numberLingos = numberLingos_tmp;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
char *smileStruct::Code(void) {

  return this->code;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
char *smileStruct::Id(void) {

  return this->id;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int smileStruct::Lingos(int numberLingos_tmp, char **lingos_tmp) {

  if (lingos_tmp) {
    for (int i = 0; i < numberLingos_tmp; i++) if (lingos_tmp[i]) delete[] lingos_tmp[i];
    delete[] lingos_tmp;
  }

  lingos_tmp = new char*[numberLingos];
  for (int i = 0; i < numberLingos; i++) {
    lingos_tmp[i] = new char[strlen(lingos[i]) + 1];
    strcpy(lingos_tmp[i], lingos[i]);
  }

  return numberLingos;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int smileStruct::NumberLingos(void) {

  return numberLingos;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::InputLine(char *line) {

  char *part = strtok(line, " ");
  if (!part) return;
  if (code) delete[] code;
  code = new char[strlen(part) + 1];
  strcpy(code, part);

  part = strtok(NULL, " ");
  if (!part) return;
  if (id) delete[] id;
  id = new char[strlen(part) + 1];
  strcpy(id, part);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int smileStruct::ObtainLingos(void) {

  if (!code) return 0;

  char *temporal_code = new char[strlen(code) + 1], *part = NULL;

  strcpy(temporal_code, code);

  if (lingos) {
    for (int i = 0; i < numberLingos; i++) if (lingos[i]) delete[] lingos[i];
    delete[] lingos;
  }
  numberLingos = 0;

  //Change of Cl and Br for
  part = strstr(temporal_code,"Cl");
  if (part) strncpy(part,"L ",2);

  while (part) {
    part = strstr(temporal_code,"Cl");
    if (part) strncpy(part,"L ",2);
  }

  part = NULL;
  part = strstr(temporal_code,"Br");
  if (part) strncpy(part,"R ",2);

  while (part) {
    part = strstr(temporal_code,"Br");
    if (part) strncpy(part,"R ",2);
  }

  for (int i = 0; i < strlen(temporal_code); i++)
    if (temporal_code[i] != ' ') temporal_code[numberLingos++] = temporal_code[i];

  temporal_code[numberLingos] = '\0';

  numberLingos -= LENGTH_LINGO - 1;

  if (numberLingos) {
    lingos = new char*[numberLingos];
    for (int i = 0; i < numberLingos; i++) {
      lingos[i] = new char[LENGTH_LINGO + 1];
      CopyLingo(lingos[i], temporal_code, i, i+4);
    }
  }
  else {
    lingos    = new char*[1];
    lingos[0] = new char[LENGTH_LINGO + 1];
    strcpy(lingos[0], temporal_code);
    numberLingos = 1;
  }

  delete[] temporal_code;

  return numberLingos;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int smileStruct::ObtainLingosWithoutNumber(void) {

  if (!code) return 0;

  char *temporal_code = new char[strlen(code) + 1], *part = NULL;

  strcpy(temporal_code, code);

  if (lingos) {
    for (int i = 0; i < numberLingos; i++) if (lingos[i]) delete[] lingos[i];
    delete[] lingos;
  }
  numberLingos = 0;

  //Change of Cl and Br for
  part = strstr(temporal_code,"Cl");
  if (part) strncpy(part,"  ",2);

  while (part) {
    part = strstr(temporal_code,"Cl");
    if (part) strncpy(part,"L ",2);
  }

  part = NULL;
  part = strstr(temporal_code,"Br");
  if (part) strncpy(part,"  ",2);

  while (part) {
    part = strstr(temporal_code,"Br");
    if (part) strncpy(part,"R ",2);
  }

  for (int i = 0; i < strlen(temporal_code); i++) {
    if (isdigit(temporal_code[i]))  temporal_code[numberLingos]   = '#';
    if (temporal_code[i] != ' ')    temporal_code[numberLingos++] = temporal_code[i];
  }

  temporal_code[numberLingos] = '\0';

  numberLingos -= LENGTH_LINGO - 1;

  if (numberLingos) {
    lingos = new char*[numberLingos];
    for (int i = 0; i < numberLingos; i++) {
      lingos[i] = new char[LENGTH_LINGO + 1];
      CopyLingo(lingos[i], temporal_code, i, i+4);
    }
  }
  else {
    lingos    = new char*[1];
    lingos[0] = new char[LENGTH_LINGO + 1];
    strcpy(lingos[0], temporal_code);
    numberLingos = 1;
  }

  delete[] temporal_code;

  return numberLingos;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
double smileStruct::ComputeLingoSimilarity(smileStruct& smileStruct_tmp) {
	
  double similarityValue = 0;

  if (!smileStruct_tmp.NumberLingos()) {
    if (!smileStruct_tmp.Code()) return -1;
    smileStruct_tmp.ObtainLingos();
  }
  if (!numberLingos) {
    if (!code) return -1;
    ObtainLingos();
  }
  int totalNumberLingos = smileStruct_tmp.numberLingos + numberLingos;
  char **totalLingos    = new char*[totalNumberLingos];

  for (int i = 0; i < totalNumberLingos; i++) {
    totalLingos[i] = new char[LENGTH_LINGO + 1];
    if (i < numberLingos) strcpy(totalLingos[i], lingos[i]);
    else                  strcpy(totalLingos[i], smileStruct_tmp.lingos[i - numberLingos]);
  }

  MergeSort(totalLingos, totalNumberLingos);
  int L = CompactList(totalLingos, totalNumberLingos);

  for (int i = 0; i < L; i++)	{
    double Nai = CountFindLingos(totalLingos[i]), Nbi = smileStruct_tmp.CountFindLingos(totalLingos[i]);
    if ((Nai - Nbi) >= 0) similarityValue += (1 - (double)((Nai - Nbi)/(Nai + Nbi)));
    if ((Nbi - Nai) >  0) similarityValue += (1 - (double)((Nbi - Nai)/(Nai + Nbi)));
  }
  similarityValue /= L;

  for (int i = 0; i < L; i++) if (totalLingos[i]) delete[] totalLingos[i];
  delete[] totalLingos;

  return similarityValue;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
double ComputeLingoSimilarity(smileStruct& smileStruct_tmp, smileStruct& smileStruct_tmp1) {
	
  double similarityValue = 0;

  if (!smileStruct_tmp.NumberLingos()) {
    if (!smileStruct_tmp.Code()) return -1;
    smileStruct_tmp.ObtainLingos();
  }
  if (!smileStruct_tmp1.NumberLingos()) {
    if (!smileStruct_tmp1.Code()) return -1;
    smileStruct_tmp1.ObtainLingos();
  }
  int totalNumberLingos = smileStruct_tmp.numberLingos + smileStruct_tmp1.numberLingos;
  char **totalLingos = new char*[totalNumberLingos];

  for (int i = 0; i < totalNumberLingos; i++) {
    totalLingos[i] = new char[LENGTH_LINGO + 1];
    if (i < smileStruct_tmp1.numberLingos) strcpy(totalLingos[i], smileStruct_tmp1.lingos[i]);
    else                                   strcpy(totalLingos[i], smileStruct_tmp.lingos[i - smileStruct_tmp1.numberLingos]);
  }

  smileStruct::MergeSort(totalLingos, totalNumberLingos);
  int L = smileStruct::CompactList(totalLingos, totalNumberLingos);

  for (int i = 0; i < L; i++)	{
    double Nai = smileStruct_tmp1.CountFindLingos(totalLingos[i]), Nbi = smileStruct_tmp.CountFindLingos(totalLingos[i]);
    if ((Nai - Nbi) >= 0) similarityValue += (1 - (double)((Nai - Nbi)/(Nai + Nbi)));
    if ((Nbi - Nai) >  0) similarityValue += (1 - (double)((Nbi - Nai)/(Nai + Nbi)));
  }
  similarityValue /= L;

  for (int i = 0; i < L; i++) if (totalLingos[i]) delete[] totalLingos[i];
  delete[] totalLingos;

  return similarityValue;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
double ComputeLingoSimilarity(smileStruct* smileStruct_tmp, smileStruct* smileStruct_tmp1) {
	
  double similarityValue = 0;

  if (!smileStruct_tmp->NumberLingos()) {
    if (!smileStruct_tmp->Code()) return -1;
    smileStruct_tmp->ObtainLingos();
  }
  if (!smileStruct_tmp1->NumberLingos()) {
    if (!smileStruct_tmp1->Code()) return -1;
    smileStruct_tmp1->ObtainLingos();
  }
  int totalNumberLingos = smileStruct_tmp->numberLingos + smileStruct_tmp1->numberLingos;
  char **totalLingos = new char*[totalNumberLingos];

  for (int i = 0; i < totalNumberLingos; i++) {
    totalLingos[i] = new char[LENGTH_LINGO + 1];
    if (i < smileStruct_tmp1->numberLingos) strcpy(totalLingos[i], smileStruct_tmp1->lingos[i]);
    else                 	            strcpy(totalLingos[i], smileStruct_tmp->lingos[i - smileStruct_tmp1->numberLingos]);
  }

  smileStruct::MergeSort(totalLingos, totalNumberLingos);
  int L = smileStruct::CompactList(totalLingos, totalNumberLingos);
  
  for (int i = 0; i < L; i++) {
    double Nai = smileStruct_tmp1->CountFindLingos(totalLingos[i]), Nbi = smileStruct_tmp->CountFindLingos(totalLingos[i]);
    if ((Nai - Nbi) >= 0) similarityValue += (1 - (double)((Nai - Nbi)/(Nai + Nbi)));
    if ((Nbi - Nai) >  0) similarityValue += (1 - (double)((Nbi - Nai)/(Nai + Nbi)));
  }

  similarityValue /= L;

  for (int i = 0; i < L; i++) if (totalLingos[i]) delete[] totalLingos[i];
  delete[] totalLingos;

  return similarityValue;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::CopyLingo(char *lingo, char* line, int start, int end) {

  int i, j;
  for (i = start, j = 0; i < end; i++) lingo[j++] = line[i];
  lingo[j] = '\0';

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int smileStruct::CountFindLingos(char *lingo_tmp) {

  if (!lingo_tmp || !strlen(lingo_tmp)) return -1;

  if (!numberLingos) {
    if (!code) return -1;
    ObtainLingos();
  }
  int countSameLingo = 0;

  for (int i = 0; i < numberLingos; i++)
    if (!strcmp(lingos[i], lingo_tmp))
      countSameLingo++;

  return countSameLingo;
	
}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::MixArrayX(char **array1, int n1, char **array2, int n2, char **array3) {

  int x1 = 0, x2 = 0, x3 = 0;

  while (x1 < n1 && x2 < n2) {
    if (array3[x3]) delete[] array3[x3];
    if (strcmp(array1[x1], array2[x2]) < 0) {
      array3[x3] = new char[strlen(array1[x1]) + 1];
      strcpy(array3[x3++], array1[x1++]);
    }
    else {
      array3[x3] = new char[strlen(array2[x2]) + 1];
      strcpy(array3[x3++], array2[x2++]);
    }
  }
  while (x1 < n1) {
    if (array3[x3]) delete[] array3[x3];
    array3[x3] = new char[strlen(array1[x1]) + 1];
    strcpy(array3[x3++], array1[x1++]);
  }
  while (x2 < n2) {
    if (array3[x3]) delete[] array3[x3];
    array3[x3] = new char[strlen(array2[x2]) + 1];
    strcpy(array3[x3++], array2[x2++]);
  }

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::MixArrayX(int *array1, int n1, int *array2, int n2, int *array3) {

  int x1 = 0, x2 = 0, x3 = 0;

  while (x1 < n1 && x2 < n2)
    if (array1[x1] < array2[x2])  array3[x3++] = array1[x1++];
    else                          array3[x3++] = array2[x2++];
  while (x1 < n1) array3[x3++] = array1[x1++];
  while (x2 < n2) array3[x3++] = array2[x2++];

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::MixArrayX(float *array1, int n1, float *array2, int n2, float *array3) {

    int x1 = 0, x2 = 0, x3 = 0;

    while (x1 < n1 && x2 < n2) 
      if (array1[x1] < array2[x2])  array3[x3++] = array1[x1++];
      else                          array3[x3++] = array2[x2++];
    while (x1 < n1) array3[x3++] = array1[x1++];
    while (x2 < n2) array3[x3++] = array2[x2++];

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::MixArray(char **array, int n) {

  char **array1, **array2;
  int n1, n2, i, j;

  if (n > 1) {

    if (n%2 == 0)   n1 = n2 = (int)n / 2;
    else         {  n1 = (int)n / 2; n2 = n1 + 1; }

    array1  = new char*[n1];
    array2  = new char*[n2];

    for (i = 0; i < n1; i++) {
      array1[i] = new char[strlen(array[i]) + 1];
      strcpy(array1[i], array[i]);
    }
    for (j = 0; j < n2; i++, j++) {
      array2[j] = new char[strlen(array[i]) + 1];
      strcpy(array2[j], array[i]);
    }

    MixArray(array1, n1);
    MixArray(array2, n2);
    MixArrayX(array1, n1, array2, n2, array);

    for (i = 0; i < n1; i++) if (array1[i]) delete[] array1[i];
    if (array1) delete[] array1;
    for (i = 0; i < n2; i++) if (array2[i]) delete[] array2[i];
    if (array2) delete[] array2;

  }

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::MixArray(int *array, int n) {

  int *array1, *array2;
  int n1, n2, i, j;

  if (n > 1) {
    if (n%2 == 0)   n1 = n2 = (int)n / 2;
    else         {  n1 = (int)n / 2; n2 = n1 + 1; }

    array1  = new int[n1];
    array2  = new int[n2];

    for (i = 0; i < n1; i++)      array1[i] = array[i];
    for (j = 0; j < n2; i++, j++) array2[j] = array[i];

    MixArray(array1, n1);
    MixArray(array2, n2);
    MixArrayX(array1, n1, array2, n2, array);

    delete[] array1;
    delete[] array2;

  }

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::MixArray(float *array, int n) {

  float *array1, *array2;
  int n1, n2, i, j;

  if (n > 1) {
      
    if (n%2 == 0)   n1 = n2 = (int)n / 2;
    else         {  n1 = (int)n / 2; n2 = n1 + 1; }

    array1  = new float[n1] ;
    array2  = new float[n2] ;

    for (i = 0; i < n1; i++)      array1[i] = array[i];
    for (j = 0; j < n2; i++, j++) array2[j] = array[i];

    MixArray(array1, n1);
    MixArray(array2, n2);
    MixArrayX(array1, n1, array2, n2, array);

    delete[] array1;
    delete[] array2;

  }

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::MergeSort(char **array, int n) {

  MixArray(array, n);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::MergeSort(int *array, int n) {

  MixArray(array, n);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void smileStruct::MergeSort(float *array, int n) {

  MixArray(array, n);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int smileStruct::CompactList(char **&array, int n) {

  if (!n) return n;

  int    nlist = 0;
  char **list  = new char*[n];

  for (int i = 0; i < n; i++) {
    list[i] = new char[LENGTH_LINGO + 1];
    if      (!i)                                     strcpy(list[nlist++], array[i]);
    else if (strcmp(list[nlist - 1], array[i]) != 0) strcpy(list[nlist++], array[i]);
  }

  for (int i = 0; i < n; i++) if (array[i]) delete[] array[i];
  delete[] array;

  array = new char*[nlist];
  for (int i = 0; i < nlist; i++) {
    array[i] = new char[LENGTH_LINGO + 1];
    strcpy(array[i], list[i]);
  }

  for (int i = 0; i < n; i++) if (list[i]) delete[] list[i];
  delete[] list;

  return nlist;
	
}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int smileStruct::CompactList(int *&array, int n) {

  if (!n) return n;

  int    nlist = 0;
  int   *list  = new int[n];

  for (int i = 0; i < n; i++) 
    if      (!i)                          list[nlist++] = array[i];
    else if (list[nlist - 1] != array[i]) list[nlist++] = array[i];

  delete[] array;

  array = new int[nlist];
  for (int i = 0; i < nlist; i++) array[i] = list[i];

  delete[] list;

  return nlist;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
smileStruct smileStruct::operator=(const smileStruct& smileStruct_tmp) {

  if (code) delete[] code;
  if (id)   delete[] id;

  if (lingos) {
    for (int i = 0; i < numberLingos; i++) if (lingos[i]) delete[] lingos[i];
    delete[] lingos;
  }

  code = new char[strlen(smileStruct_tmp.code) + 1];
  strcpy(code, smileStruct_tmp.code);
  id = new char[strlen(smileStruct_tmp.id) + 1];
  strcpy(id, smileStruct_tmp.id);

  numberLingos = smileStruct_tmp.numberLingos;

  lingos = new char*[numberLingos];
  for (int i = 0; i < numberLingos; i++) {
    lingos[i] = new char[strlen(smileStruct_tmp.lingos[i]) + 1];
    strcpy(lingos[i], smileStruct_tmp.lingos[i]);
  }

  return *this;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
